【インスタンスの止め忘れを防ごう】MacのAutomatorとlaunchdで実行中のEC2をリマインドする
こんにちは、虎塚です。
起動したEC2インスタンスをうっかり停止し忘れて、思ったよりも高額の請求がきてしまった……なんてこと、ありませんか?
予期しない出費を避けるために、最近は実行中のEC2インスタンスを自分に対してリマインドするようにしています。今日はその方法をご紹介します。
実現すること
毎日10時と18時に、起動中のEC2インスタンスのインスタンスIDを画面に通知します。動作イメージは次のような感じです。
動作確認した環境
Mac OS Xが前提です。次の環境で動作確認しました。
- Mac OS X 10.9.5 (13F1066)
- Automator 2.4 (381)
- aws-cli 1.7.14
使用するツールの紹介
手元のツールを組み合わせて、簡単に作ることができます。
- Automator
- Mac上での作業の自動化を支援するOS標準ツールです。ドラッグアンドドロップで様々なアクションをつなぎあわせて、ワークフローやアプリケーションを作成できます。
- launchd
- ジョブを定期実行するためのOS標準ツールです。最近のMacでは、cronの代わりにlaunchdを使うことが推奨されています。.plist(プロパティリスト)という設定ファイルにプログラムの実行方法を定義し、launchctlコマンドで読み込んで使います。
- .plistファイルは、XMLで記述します。といっても、GUIから設定できるツールもありますので安心してください。でも、今回はテキストエディタで編集します。
- スクリプト言語の実行環境
- Automatorの「シェルスクリプトを実行」アクションで実行できるスクリプトの実行環境が必要です。シェルスクリプト(bash、csh、ksh、sh、tcsh、zsh)のほかに、Perl、Python、Rubyが利用できます。
- 今回はzshシェルを使います。
リマインダー作成手順
おおまかな流れは次のとおりです。順に見ていきましょう。
- Automatorでアプリケーションを作成する
- AWS CLIを使って実行中のインスタンスの情報を取得する
- 実行中のインスタンス情報を画面通知する
- 作成したアプリケーションをlaunchdで定期実行する
1. Automatorでアプリケーションを作成する
まず、Automatorを起動します。Launchpadで「Auto」を検索して実行するか、アプリケーションフォルダ直下にあるAutomator.appを実行します。
新規作成のダイアログが開くので(開かない場合は、[ファイル]→[新規作成]を選ぶと開きます)、[書類の種類を選択してください]のボックスで「アプリケーション」をクリックし、[選択]ボタンを押します。
次に、アクションを順に並べます。アクション一覧から「シェルスクリプトを実行」を選んで、右ペインにドラッグアンドドロップします。
[シェル]のドロップダウンリストで「/bin/zsh」を選んでおきます。初期値として表示されている「cat」を消去して、代わりに次のコマンドを入力します。
aws --output json ec2 describe-instances --filter "Name=instance-state-name,Values=running" | jq '.Reservations[].Instances[].InstanceId'
AWS CLIを実行するために必要なアクセスキー、シークレットアクセスキー、デフォルトのリージョンは、設定ファイルで事前に与えているものとします。このあたりの設定が分からない場合は、「AWSで構築した環境にありがちなシェルスクリプトたち まとめ | Developers.IO」のセットアップの項目を参照してください。
ここで、右上の実行ボタンを押して、「シェルスクリプトを実行」アクションの動作を確認しておきましょう。次のようなログが出力されれば、成功です。
そして、上記のアクションの結果を確認ウィンドウへ渡すために、変数に格納します。アクション一覧から「変数の値を設定」を選んで、右ペインにドラッグアンドドロップします。[変数]ドロップダウンリストに表示されている「新規変数」をクリックします。
すると、[変数オプション]ダイアログが開きます。任意の変数名(instances)を入力して、[完了]ボタンを押します。デフォルトの変数名は「記憶装置」です。
さらに、確認ウィンドウを開くアクションを追加します。アクション一覧から「確認を求める」を選んで、右ペインにドラッグアンドドロップします。[メッセージ]という文字が表示されている欄に、変数ペインから先ほど作成した変数(instance)をドラッグアンドドロップします。
「通知を表示」アクションでも良いですが、通知は表示領域が小さいことと、数秒たつと自動的に非表示になってしまうため、今回は「確認を求める」アクションにしました。確認ダイアログの方が目立ちますし、ユーザがボタンを押すまでは画面から消えません。さりげないリマインダーをお求めの方は、「通知を表示」を使用してください。
さて、ふたたび右上の[実行]ボタンを押して、「確認を求める」アクションの動作を確認しておきましょう。あらかじめEC2インスタンスを3個起動し、そのうち1つは停止状態にしておきます。作成中のアプリケーションを実行して、次のようにダイアログが表示されると成功です。
実行中(running)のインスタンスのinstance idだけが表示されていることを確認してください。
[OK]ボタンを押すと、アプリケーションが最後まで実行されます。[キャンセル]ボタンを押すと、このアクションでアプリケーションが終了します。今回はアプリケーションの最後に実行されるアクションが「確認を求める」アクションなので、どちらのボタンを押してもユーザに見える結果は同じです。
最後に、アプリケーションを保存します。[ファイル]→[保存]で[フォーマット]に「アプリケーション」を選んで、任意の場所に保存します。今回は、USER_HOME/bin/automator/aws_check_instance.appとして保存しました。
2. 作成したアプリケーションをlaunchdで定期実行する
手順1で作成したaws_check_instance.appを定期実行するジョブを設定します。
まず、~/Library/LaunchAgents/ディレクトリに、任意の名前(aws.check.instance.plist)でplistファイルを作成します。
<?xml version="1.0" encoding="UTF-8"?> <!DOCTYPE plist PUBLIC "-//Apple//DTD PLIST 1.0//EN" "http://www.apple.com/DTDs/PropertyList-1.0.dtd"> <plist version="1.0"> <dict> <key>Label</key> <string>aws.check.instance</string> <key>Program</key> <string>/Users/{USER_NAME}/bin/automator/aws_check_instance.app/Contents/MacOS/Application Stub</string> <key>StartCalendarInterval</key> <array> <dict> <key>Hour</key> <integer>10</integer> <key>Minute</key> <integer>0</integer> </dict> <dict> <key>Hour</key> <integer>18</integer> <key>Minute</key> <integer>0</integer> </dict> </array> </dict> </plist>
- Label(必須)
- 慣習的にplistファイル名と同じ名前を指定します
- Program(必須)
- 実行するプログラムへの絶対パスを指定します。引数を取るプログラムの場合は、ProgramArgumentsという要素を使います。詳しくはマニュアルを参照してください。
- アプリケーション(.app)を指定するときは、上のように{APPLICATION_NAME}/Contents/MacOS/Application Stubと指定します。パスに空白を含みますが、ダブルクォートで囲む必要はありません。
- StartCalendarInterval
- Month、Day、Weekday、Hour、Minuteのkeyを組み合わせて、実行スケジュールを定義します。
- keyを指定しない項目は、ワイルドカードとして扱われることに注意してください。たとえば、上のplistでMinuteの設定を除去すると、10時台と18時台の毎分にアプリケーションが実行されます。
動作確認のために、設定から数分後に実行するスケジュールも定義しておくとよいでしょう。
また、今回は使用しませんが、一定時間(秒)ごとに実行するジョブを次のように定義できます。StartIntervalをStartCalendarIntervalの代わりに使用します。
<key>StartInterval</key> <integer>3600</integer>
上の設定では、1時間に1回ジョブを実行します。
次に、plistをlaunchdに読み込ませます。
launchctl load ~/Library/LaunchAgents/aws.check.instance.plist
何も表示されなければ成功です。登録したジョブの状態は、次のコマンドで確認できます。
% launchctl list aws.check.instance { "Label" = "aws.check.instance"; "LimitLoadToSessionType" = "Aqua"; "OnDemand" = true; "LastExitStatus" = 0; "TimeOut" = 30; "Program" = "/Users/{USER_NAME}/bin/automator/aws_check_instance.app/Contents/MacOS/Application Stub"; };
最後に、動作確認をします。定義したスケジュールどおりに確認ダイアログが表示されれば、成功です。
確認ダイアログが表示されない場合は、ジョブの状態をチェックするコマンドを実行してみましょう。LastExitStatusが0以外になっている場合、何らかのエラーが発生しています。
plistファイルにStandardOutPathとStandardErrorPathというkeyを定義することで、デバッグができます。詳しくは、A launchd Tutorialを参照してください。
なお、plistの内容を編集した場合は、次のコマンドで一旦読み込みを解除してから、もう一度読み込んでください。
launchctl unload ~/Library/LaunchAgents/aws.check.instance.plist
注意点
EC2インスタンスの料金は、2015年3月現在、1時間単位の従量課金です。つまり、5分間だけ起動した場合も59分間ずっと起動した場合も、発生する料金は同じです。
そして、1時間のうちに2回起動してしまうと、請求も2倍になることに注意してください。こまめなインスタンス停止は節約になりますが、細かくやりすぎると逆効果です。
参考資料
- jq Manual
- Automatorアプリケーションに付属のヘルプ
- A launchd Tutorial
おわりに
急ごしらえのリマインダーのご紹介でした。これでインスタンスの停止忘れを防いで、無駄遣いを減らせるとうれしいですね。AWS APIが提供されているおかげで、ユーザがこのように対処できるのは大変助かります。
もっとこうするといい感じのリマインダーになるよ!というような情報がありましたら、ぜひお寄せいただければと思います。お待ちしております。
それでは、また。